import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flutter_leveldb/status_code.dart';

class FlutterLeveldb {
  String dbName = "default";
  LevelDB levelDB;

  static final Map<String, FlutterLeveldb> _cache = <String, FlutterLeveldb>{};

  FlutterLeveldb._internal(this.dbName, this.levelDB);

  static Future<FlutterLeveldb> db(String name) async {
    if (_cache.containsKey(name)) {
      return _cache[name];
    } else {
//      LevelDB openDb = await LevelDB.open("levelDb/$name");
      LevelDB openDb = new LevelDB();
      final db = new FlutterLeveldb._internal(name, openDb);
      _cache[name] = db;
      return db;
    }
  }

  Future<T> get<T>(String key) async {
    if (T == int) {
      return await levelDB.getInt(key) as T;
    } else if (T == String) {
      return await levelDB.getString(key) as T;
    } else if (T == bool) {
      return await levelDB.getBoolean(key) as T;
    } else if (T == double) {
      return await levelDB.getDouble(key) as T;
    } else {
      ///TODO 对象序列以及二进制处理
      return null;
    }
  }

  Future put(String key, dynamic value) async {
    if (value.runtimeType == int) {
      await levelDB.putInt(key, value);
    } else if (value.runtimeType == String) {
      await levelDB.putString(key, value);
    } else if (value.runtimeType == bool) {
      await levelDB.putBoolean(key, value);
    } else if (value.runtimeType == double) {
      await levelDB.putDouble(key, value);
    } else {
      ///Uint8List	byte[]	FlutterStandardTypedData typedDataWithBytes:
      ///Int32List	int[]	FlutterStandardTypedData typedDataWithInt32:
      ///Int64List	long[]	FlutterStandardTypedData typedDataWithInt64:
      ///Float64List	double[]	FlutterStandardTypedData typedDataWithFloat64:
      ///List	java.util.ArrayList	NSArray
      ///Map	java.util.HashMap	NSDictionary
      ///TODO 对象序列以及二进制处理
    }
  }
}

class LevelDB {
  static const MethodChannel _channel = const MethodChannel('flutter_leveldb');

  int dbPointer = 0;

  static void throwException() async {
    int errorCode = await _channel.invokeMethod('getError');
    var msg = StatusCode.getMessage(errorCode);
    throw new Exception("code:$errorCode,message:$msg");
  }

  static Future<LevelDB> open(
    String path, {
    bool shared: false,
    int blockSize: 4096,
    bool createIfMissing: true,
    bool errorIfExists: false,
  }) async {
    var dbPointer = await _channel.invokeMethod('open', <String, dynamic>{
      "shared": shared,
      "path": path,
      "blockSize": blockSize,
      "createIfMissing": createIfMissing,
      "errorIfExists": errorIfExists
    });
    if (dbPointer > 0) {
      LevelDB db = new LevelDB();
      db.dbPointer = dbPointer;
      return db;
    } else {
      throwException();
      return null;
    }
  }

  Future close() async {
    await _channel.invokeMethod('close', <String, dynamic>{"db": dbPointer});
  }

  Future<String> getString(String key) async {
    String value = await _channel.invokeMethod(
        'getString', <String, dynamic>{"db": this.dbPointer, "key": key});
    return value;
  }

  Future putString(String key, String value) async {
    await _channel.invokeMethod(
        'putString', <String, dynamic>{"db": this.dbPointer, "key": key});
  }

  Future<int> getInt(String key) async {
    ///不想去确定int长度,1.bitLength 这样可以确定长度
    int value = await _channel.invokeMethod(
        'getLong', <String, dynamic>{"db": this.dbPointer, "key": key});
    return value;
  }

  Future putInt(String key, int value) async {
    await _channel.invokeMethod(
        'putLong', <String, dynamic>{"db": this.dbPointer, "key": key});
  }

  Future<bool> getBoolean(String key) async {
    bool value = await _channel.invokeMethod(
        'getBoolean', <String, dynamic>{"db": this.dbPointer, "key": key});
    return value;
  }

  Future putBoolean(String key, bool value) async {
    await _channel.invokeMethod(
        'putBoolean', <String, dynamic>{"db": this.dbPointer, "key": key});
  }

  Future<double> getDouble(String key) async {
    double value = await _channel.invokeMethod(
        'getDouble', <String, dynamic>{"db": this.dbPointer, "key": key});
    return value;
  }

  Future putDouble(String key, double value) async {
    await _channel.invokeMethod(
        'putDouble', <String, dynamic>{"db": this.dbPointer, "key": key});
  }

  Future delete(String key) async {
    await _channel.invokeMethod(
        'delete', <String, dynamic>{"db": this.dbPointer, "key": key});
  }
}
